<template>
    <div 
      ref="container" 
      class="virtual-scroll-container"
      @scroll="handleScroll"
    >
      <div 
        class="virtual-scroll-phantom" 
        :style="{ height: totalHeight + 'px' }"
      ></div>
      <div 
        class="virtual-scroll-content"
        :style="{ transform: `translateY(${startOffset}px)` }"
      >
        <slot 
          v-for="item in visibleData" 
          :key="item.index"
          :item="item.data"
          :index="item.index"
        ></slot>
      </div>
    </div>
  </template>
  
  <script setup>
  import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
  
  const props = defineProps({
    // 列表数据
    data: {
      type: Array,
      required: true
    },
    // 每项高度
    itemHeight: {
      type: Number,
      required: true
    },
    // 缓冲区大小
    buffer: {
      type: Number,
      default: 5
    }
  })
  
  const container = ref(null)
  const scrollTop = ref(0)
  const clientHeight = ref(0)
  
  // 总高度
  const totalHeight = computed(() => props.data.length * props.itemHeight)
  
  // 可见区域起始索引
  const startIndex = computed(() => {
    return Math.max(0, Math.floor(scrollTop.value / props.itemHeight) - props.buffer)
  })
  
  // 可见区域结束索引
  const endIndex = computed(() => {
    return Math.min(
      props.data.length,
      Math.ceil((scrollTop.value + clientHeight.value) / props.itemHeight) + props.buffer
    )
  })
  
  // 可见数据
  const visibleData = computed(() => {
    return props.data
      .slice(startIndex.value, endIndex.value)
      .map((item, index) => ({
        data: item,
        index: startIndex.value + index
      }))
  })
  
  // 偏移量
  const startOffset = computed(() => {
    return startIndex.value * props.itemHeight
  })
  
  // 滚动处理
  const handleScroll = () => {
    if (!container.value) return
    scrollTop.value = container.value.scrollTop
  }
  
  // 初始化
  onMounted(() => {
    if (!container.value) return
    clientHeight.value = container.value.clientHeight
    
    // 监听容器大小变化
    const observer = new ResizeObserver((entries) => {
      for (const entry of entries) {
        clientHeight.value = entry.contentRect.height
      }
    })
    
    observer.observe(container.value)
    
    onBeforeUnmount(() => {
      observer.disconnect()
    })
  })
  </script>
  
  <style scoped>
  .virtual-scroll-container {
    height: 100%;
    overflow-y: auto;
    position: relative;
  }
  
  .virtual-scroll-phantom {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    z-index: -1;
  }
  
  .virtual-scroll-content {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    min-height: 100%;
  }
  </style>